﻿using System;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace parts_client
{
	public class ServerConnection
	{
		protected Socket _Socket = null;
		protected Thread _CommThread = null;
		protected bool _KeepCommunicating = true;

        protected Stopwatch _Stopwatch;
        protected int _NumSteps;
		
		protected Byte[] _RecvBuff = null;
		protected float[] _XBuff = null;
		protected float[] _YBuff = null;

		public ServerConnection(String serverAddr, int port)
		{
			IPHostEntry hostEntry = Dns.GetHostEntry(serverAddr);
			foreach (IPAddress addr in hostEntry.AddressList)
			{
				IPEndPoint ipe = new IPEndPoint(addr, port);
				try
				{
					Socket s = new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
					s.Connect(ipe);
					if (s.Connected)
					{
						_Socket = s;
                        //_Socket.ReceiveBufferSize = 2000000;
						break;
					}
				}
				catch (Exception) { }
			}

			if (this.IsConnected)
			{
				_CommThread = new Thread(new ThreadStart(this.ThreadLoop));
				_CommThread.Start();
			}
		}

		public bool IsConnected
		{
			get { return _Socket != null; }
		}

		public void Close()
		{
			_KeepCommunicating = false;
			_CommThread.Join();
			_Socket.Close();
			_CommThread = null;
			_Socket = null;
		}

		protected void ThreadLoop()
		{
            _Stopwatch = new Stopwatch();
            _Stopwatch.Start();
            _NumSteps = 0;

			this.GetBounds();

			while (_KeepCommunicating)
			{
				//this.GetParts();
				this.GetFrame ();
                _NumSteps++;

                if (_NumSteps % 2500 == 0)
                {
                    double avgStepTime = _Stopwatch.ElapsedMilliseconds / 2500.0;
                    Debug.WriteLine("Receiving " + (1000.0 / avgStepTime).ToString("n4") + " steps per second");
                    _Stopwatch.Reset();
					_Stopwatch.Start();
                }
			}

			this.CloseSession();
		}

		protected void GetBounds()
		{
			try
			{
				_Socket.Send(Encoding.ASCII.GetBytes("get-bounds"));
				Byte[] response = new Byte[16];
				int received = _Socket.Receive(response);
				float minx = BitConverter.ToSingle(response, 0);
				float maxx = BitConverter.ToSingle(response, 4);
				float miny = BitConverter.ToSingle(response, 8);
				float maxy = BitConverter.ToSingle(response, 12);
				SimData.UpdateWorldBounds(minx, maxx, miny, maxy);
			}
			catch (SocketException) { }
		}

		protected void GetParts()
		{
			try
			{
				_Socket.Send(Encoding.ASCII.GetBytes("get-parts"));
				
				if(_RecvBuff == null || _RecvBuff.Length != _Socket.ReceiveBufferSize)
				{
					_RecvBuff = new Byte[_Socket.ReceiveBufferSize];
				}
				
				int received = _Socket.Receive(_RecvBuff);
				int n = BitConverter.ToInt32(_RecvBuff, 0);
				int totallength = sizeof(int) + 2 * n * sizeof(float);

				while (received < totallength)
				{
					received += _Socket.Receive(_RecvBuff, received, _RecvBuff.Length - received, SocketFlags.None);
				}
				
				if (_XBuff == null || _XBuff.Length != n ||
					_YBuff == null || _YBuff.Length != n)
				{
					_XBuff = new float[n];
					_YBuff = new float[n];
				}
				
				for (int i = 0; i < n; i++)
				{
					_XBuff[i] = BitConverter.ToSingle(_RecvBuff, sizeof(int) + (2 * i) * sizeof(float));
					_YBuff[i] = BitConverter.ToSingle(_RecvBuff, sizeof(int) + (2 * i + 1) * sizeof(float));
				}
				
				SimData.UpdateParts(n, _XBuff, _YBuff);
			}
			catch (SocketException) { }
		}
		
		protected void GetFrame()
		{
			try
			{
				_Socket.Send (Encoding.ASCII.GetBytes ("get-frame"));	
				
				byte[] dimRecvBuff = new byte[8];
				int received = _Socket.Receive(dimRecvBuff);
				int width = BitConverter.ToInt32(dimRecvBuff, 0);
				int height = BitConverter.ToInt32(dimRecvBuff, 4);
				
				if(_RecvBuff == null || _RecvBuff.Length != width * height * 4)
				{
					_RecvBuff = new byte[width*height*4];
				}
				
				received = 0;

				while (received < _RecvBuff.Length)
				{
					received += _Socket.Receive(_RecvBuff, received, _RecvBuff.Length - received, SocketFlags.None);
				}
				
				SimData.UpdateFrame(_RecvBuff, width, height);
			}
			catch (SocketException) { }
		}

		public void CloseSession()
		{
			try
			{
				_Socket.Send(Encoding.ASCII.GetBytes("close-session"));
			}
			catch (SocketException) { }
		}
	}
}
	